/*
 * Created on 2004-04-23
 */
package org.opencube.data;

import java.util.Properties;

import javax.xml.rpc.Stub;

import org.opencube.data.ws.stub.BoundVariable;
import org.opencube.data.ws.stub.CallResult;
import org.opencube.data.ws.stub.Column;
import org.opencube.data.ws.stub.DataSourceService;
import org.opencube.data.ws.stub.Dservice_Impl;
import org.opencube.data.ws.stub.Record;
import org.opencube.data.ws.stub.RecordSet;
import org.opencube.data.ws.stub.RecordSetMetaData;

/**
 * @author <a href="mailto:maciek@fingo.pl">FINGO - Maciej Mroczko</a>
 * TODO: comment
 */
public class WSClient extends DataSource
{
	private DataSourceService service = null;

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#connect(java.util.Properties)
	 */
	public void connect( Properties properties ) throws Exception
	{
		super.connect( properties );
		getServiceProxy( true );
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#executeCall(java.lang.String, java.util.HashMap)
	 */
	public org.opencube.data.CallResult executeCall( String statement, org.opencube.data.BoundVariable[] bindVariables)
	throws Exception
	{
		return mapCallResultTo( getServiceProxy( false ).executeCall( statement, mapVariablesFrom( bindVariables ) ) );
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#executeQuery(java.lang.String)
	 */
	public org.opencube.data.RecordSet executeQuery( String query ) throws Exception
	{
		return mapRecordSetTo( getServiceProxy( false ).executeQuery( query ) );
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#execute(java.lang.String)
	 */
	public boolean execute( String statement ) throws Exception
	{
		return getServiceProxy( false ).execute( statement );
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#executeUpdate(java.lang.String)
	 */
	public int executeUpdate( String statement ) throws Exception
	{
		return getServiceProxy( false ).executeUpdate( statement );
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#isAutoCommit()
	 */
	public boolean isAutoCommit() throws Exception
	{
		return getServiceProxy( false ).isAutoCommit();
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#setAutoCommit(boolean)
	 */
	public void setAutoCommit( boolean autoCommit ) throws Exception
	{
		getServiceProxy( false ).setAutoCommit( autoCommit );
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#commit()
	 */
	public void commit() throws Exception
	{
		getServiceProxy( false ).commit();

	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#rollback()
	 */
	public void rollback() throws Exception
	{
		getServiceProxy( false ).rollback();

	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#getType()
	 */
	public String getType()
	{
		return "oracle"; 
	}
	
	public static void main( String[] args ) 
	{
		try 
		{
			WSClient ws = new WSClient();
			ws.connect( null );
			ws.executeCall( 
				"begin " + 
					"open :out_cursor_meta_data for " + 
						"select " +
							"* " +
						"from " +
							"namespace " +
					        "left outer join datatype on dat_nsp_id = nsp_id " + 
							"left outer join datatypeenumeration on den_dat_id = dat_id " + 
							"left outer join scheme on scm_nsp_id = nsp_id " +
							"left outer join simpleattribute on sat_scm_id = scm_id " +
							"left outer join complexattribute on cat_source_scm_id = scm_id " +
						"order by  " +
							"scm_id; " +
				"end;", 
				new org.opencube.data.BoundVariable[]{ new org.opencube.data.BoundVariable( "out_cursor_meta_data", null ) } );
		} 
		catch( Exception ex ) 
		{
			ex.printStackTrace();
		}
	}
	 
	private Stub createProxy()
	{
		return (Stub) new Dservice_Impl().getDataSourceServicePort();	
	}
	
	private DataSourceService getServiceProxy( boolean create )
	throws Exception 
	{
		if( this.service == null )
		{
			if( create )
			{
				testProperty( this.url, PROPERTY_URL );
				testProperty( this.username, PROPERTY_USERNAME );
				testProperty( this.password, PROPERTY_PASSWORD );
				Stub stub = createProxy();
				stub._setProperty( Stub.ENDPOINT_ADDRESS_PROPERTY, this.url );
				stub._setProperty( Stub.USERNAME_PROPERTY, this.username );
				stub._setProperty( Stub.PASSWORD_PROPERTY, this.password );
				stub._setProperty( Stub.SESSION_MAINTAIN_PROPERTY, Boolean.TRUE );
				this.service = (DataSourceService) stub;
			}
			else
			{
				throw new Exception( "WSClient not connected" );
			}
		}
		return this.service;
	}

	private org.opencube.data.Record mapRecordTo( Record record )
	{
		org.opencube.data.Record result = null;
		if( record != null )
		{
			result = new org.opencube.data.Record( record.getValues() );
		}
		
		return result;
	}

	private org.opencube.data.Column mapColumnTo( Column column )
	{
		return column == null ? null : new org.opencube.data.Column( column.getName() );
	}
	
	private org.opencube.data.RecordSetMetaData mapMetaDataTo( RecordSetMetaData metaData )
	{
		org.opencube.data.RecordSetMetaData result = null;
		if( metaData != null )
		{
			result = new org.opencube.data.RecordSetMetaData();
			Column[] columns = metaData.getColumns();
			for( int i = 0; columns != null && i < columns.length; i++ )
			{
				result.addColumn( mapColumnTo( columns[ i ] ) );
			}
		}
		
		return result;
	}
	
	private org.opencube.data.RecordSet mapRecordSetTo( RecordSet recordSet )
	{
		org.opencube.data.RecordSet result = null;
		if( recordSet != null )
		{
			result = new org.opencube.data.RecordSet();
			result.setMetaData( mapMetaDataTo( recordSet.getMetaData() ) );
			Record[] records = recordSet.getRecords();
			for( int i = 0; records != null && i < records.length; i++ )
			{
				result.addRecord( mapRecordTo( records[ i ] ) );
			} 
		}
		
		return result;
	}

	private BoundVariable[] mapVariablesFrom( org.opencube.data.BoundVariable[] variables )
	{
		BoundVariable[] result = null;
		if( variables != null )
		{
			result = new BoundVariable[ variables.length ];
			for( int i = 0; i < variables.length; i++ )
			{
				result[ i ] = new BoundVariable( variables[ i ].getName(), variables[ i ].getValue() );
			}
		}
		
		return result;
	}
	
	private BoundVariable[][] mapVariablesFrom( org.opencube.data.BoundVariable[][] variables )
	{
		BoundVariable[][] result = null;
		if( variables != null )
		{
			result = new BoundVariable[ variables.length ][];
			for( int i = 0; i < variables.length; i++ )
			{
				if( variables[ i ] != null )
				{
					result[ i ] = new BoundVariable[ variables[ i ].length ];
					for (int j = 0; variables[ i ] != null && j < variables[ i ].length; j++)
					{
						result[ i ][ j ] = new BoundVariable( variables[ i ][ j ].getName(), variables[ i ][ j ].getValue() );
					}
				}
			}
		}
		
		return result;
	}

	private org.opencube.data.BoundVariable mapBoundVariableTo( BoundVariable variable )
	{
		if( variable != null )
		{
			return new org.opencube.data.BoundVariable( variable.getName(), variable.getValue() );
		}
		return null;
	}
	
	private org.opencube.data.CallResult mapCallResultTo( CallResult callResult )
	{
		org.opencube.data.CallResult result = null;
		if( callResult != null )
		{
			result = new org.opencube.data.CallResult();
			BoundVariable[] bvs = callResult.getBoundVariables();
			RecordSet[] rss = callResult.getRecordSets();
			// bound variables
			for( int i = 0; bvs != null && i < bvs.length; i++ )
			{
				result.addBoundVariable( mapBoundVariableTo( bvs[ i ] ) );
			}
			
			//record sets
			for( int i = 0; rss != null && i < rss.length; i++ )
			{
				result.addRecordSet( mapRecordSetTo( rss[ i ] ) );
			}
		}
		return result;
	}
	
	private void testProperty( String value, String name ) throws Exception
	{
		if( value == null || value.length() == 0 )
		{
			throw new Exception( "The property '" + name+ "' is not set" );
		}
	}
	
	public int executeBatch(String statement, org.opencube.data.BoundVariable[][] boundVariables ) 
	throws Exception
	{
		return getServiceProxy( false ).executeBatch( statement, mapVariablesFrom( boundVariables ) );
	}

	public boolean supportsBatching() throws Exception
	{
		return getServiceProxy( false ).supportsBatching();
	}
	
	public int getMinimumBatchSize() throws Exception
	{
		return getServiceProxy( false ).getMinimumBatchSize();
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#execute(java.lang.String, java.lang.String, java.lang.String)
	 */
	@Override
	public boolean execute( String statement, String namespace, String resourceKey ) throws Exception
	{
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#executeBatch(java.lang.String, org.opencube.data.BoundVariable[][], java.lang.String, java.lang.String)
	 */
	@Override
	public int executeBatch( String statement, org.opencube.data.BoundVariable[][] vars, String namespace, String resourceKey ) throws Exception
	{
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#executeCall(java.lang.String, org.opencube.data.BoundVariable[], java.lang.String, java.lang.String)
	 */
	@Override
	public org.opencube.data.CallResult executeCall( String statement, org.opencube.data.BoundVariable[] bindVariables, String namespace, String resourceKey ) throws Exception
	{
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#executeQuery(java.lang.String, java.lang.String, java.lang.String)
	 */
	@Override
	public org.opencube.data.RecordSet executeQuery( String query, String namespace, String resourceKey ) throws Exception
	{
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see org.opencube.data.DataSource#executeUpdate(java.lang.String, java.lang.String, java.lang.String)
	 */
	@Override
	public int executeUpdate( String statement, String namespace, String resourceKey ) throws Exception
	{
		throw new UnsupportedOperationException();
	}	
}
